// TODO: This should come from Rust via Specta
export type OperationType =
	| "query"
	| "mutation"
	| "subscription"
	| "subscriptionStop";

// TODO
export type ProcedureDef = { key: string; input: any; result: any };

/**
 * This type represents the Typescript bindings which are generated from the router by Rust.
 */
export type ProceduresDef = {
	queries: ProcedureDef;
	mutations: ProcedureDef;
	subscriptions: ProcedureDef;
};

/**
 * A type which allows inferring the type of the bindings
 */
export type ProceduresLike =
	| {
			_rspc_def: ProceduresDef;
	  }
	| ProceduresDef;

/**
 * This is a helper method to infer the type of bindings for this router off other rspc types.
 * The inferred type is what is generated by Rust.
 * @example
 * const rspc = createReactQueryHooks<Operations>();
 * type Bindings = inferBindingsType<typeof rspc>
 */
export type inferProcedures<TProcedures extends ProceduresLike> =
	TProcedures extends { _rspc_def: ProceduresDef }
		? TProcedures["_rspc_def"]
		: TProcedures;

// TODO
export type inferProcedureKey<
	TProcedures extends ProceduresLike,
	TOperation extends keyof ProceduresDef,
> = inferProcedures<TProcedures>[TOperation]["key"];

// TODO
export type inferProcedure<
	TProcedures extends ProceduresLike,
	TOperation extends keyof ProceduresDef,
	K extends inferProcedureKey<TProcedures, TOperation>,
> = Extract<inferProcedures<TProcedures>[TOperation], { key: K }>;

// TODO
export type inferProcedureInput<
	TProcedures extends ProceduresLike,
	TOperation extends keyof ProceduresDef,
	K extends inferProcedureKey<TProcedures, TOperation>,
> = inferProcedure<TProcedures, TOperation, K>["input"];

// TODO
export type inferProcedureResult<
	TProcedures extends ProceduresLike,
	TOperation extends keyof ProceduresDef,
	K extends inferProcedureKey<TProcedures, TOperation>,
> = inferProcedure<TProcedures, TOperation, K>["result"];

// TODO
export type _inferProcedureHandlerInput<
	TProcedures extends ProceduresLike,
	TOperation extends keyof ProceduresDef,
	K extends inferProcedures<TProcedures>[TOperation]["key"],
> = inferProcedure<TProcedures, TOperation, K>["input"] extends never
	? []
	: [inferProcedure<TProcedures, TOperation, K>["input"]];

export type inferQueries<TProcedures extends ProceduresLike> =
	inferProcedures<TProcedures>["queries"];

export type inferQuery<
	TProcedures extends ProceduresLike,
	K extends inferQueries<TProcedures>["key"],
> = Extract<inferQueries<TProcedures>, { key: K }>;

// TODO
export type inferQueryInput<
	TProcedures extends ProceduresLike,
	T extends inferQueries<TProcedures>["key"][0],
> = inferProcedureInput<inferProcedures<TProcedures>, "queries", T>;

// TODO
export type inferQueryResult<
	TProcedures extends ProceduresLike,
	T extends inferQueries<TProcedures>["key"][0],
> = inferProcedureResult<inferProcedures<TProcedures>, "queries", T>;

export type inferMutations<TProcedures extends ProceduresLike> =
	inferProcedures<TProcedures>["mutations"];

export type inferMutation<
	TProcedures extends ProceduresLike,
	K extends inferMutations<TProcedures>["key"],
> = Extract<inferMutations<TProcedures>, { key: K }>;

// TODO
export type inferMutationInput<
	TProcedures extends ProceduresLike,
	T extends inferMutations<TProcedures>["key"][0],
> = inferProcedureInput<inferProcedures<TProcedures>, "mutations", T>;

// TODO
export type inferMutationResult<
	TProcedures extends ProceduresLike,
	T extends inferMutations<TProcedures>["key"][0],
> = inferProcedureResult<inferProcedures<TProcedures>, "mutations", T>;

export type inferSubscriptions<TProcedures extends ProceduresLike> =
	inferProcedures<TProcedures>["subscriptions"];

export type inferSubscription<
	TProcedures extends ProceduresLike,
	K extends inferSubscriptions<TProcedures>["key"],
> = Extract<inferSubscriptions<TProcedures>, { key: K }>;

// TODO
export type inferSubscriptionInput<
	TProcedures extends ProceduresLike,
	T extends inferSubscriptions<TProcedures>["key"][0],
> = inferProcedureInput<inferProcedures<TProcedures>, "subscriptions", T>;

// TODO
export type inferSubscriptionResult<
	TProcedures extends ProceduresLike,
	T extends inferSubscriptions<TProcedures>["key"][0],
> = inferProcedureResult<inferProcedures<TProcedures>, "subscriptions", T>;

// TODO

export type inferInfiniteQueries<TProcedures extends ProceduresLike> = Exclude<
	Extract<inferProcedures<TProcedures>["queries"], { input: { cursor: any } }>,
	{ input: never }
>;

// TODO
export type inferInfiniteQuery<
	TProcedures extends ProceduresLike,
	K extends inferInfiniteQueries<TProcedures>["key"],
> = Extract<inferInfiniteQueries<TProcedures>, { key: K }>;

// TODO
type EmptyObjToNever<T> = keyof T extends never ? never : T;
export type inferInfiniteQueryInput<
	TProcedures extends ProceduresLike,
	K extends inferInfiniteQueries<TProcedures>["key"],
> = EmptyObjToNever<
	Omit<inferInfiniteQuery<TProcedures, K>["input"], "cursor">
>;

// TODO
export type inferInfiniteQueryResult<
	TProcedures extends ProceduresLike,
	K extends inferInfiniteQueries<TProcedures>["key"],
> = inferInfiniteQuery<TProcedures, K>["result"];

// TODO
export type _inferInfiniteQueryProcedureHandlerInput<
	TProcedures extends ProceduresLike,
	K extends inferInfiniteQueries<TProcedures>["key"],
> =
	inferInfiniteQueryInput<TProcedures, K> extends never
		? []
		: [inferInfiniteQueryInput<TProcedures, K>];

// TODO: Extracting subset of operations by name or some shared key
